package com.deyuanyun.pic.wf.service.impl;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.deyuanyun.pic.common.util.StringUtils;
import com.deyuanyun.pic.common.util.web.Asserts;
import com.deyuanyun.pic.settings.domain.util.DomainComparator;
import com.deyuanyun.pic.settings.domain.util.DomainInitUtil;
import com.deyuanyun.pic.wf.domain.Node;
import com.deyuanyun.pic.wf.domain.NodeExample;
import com.deyuanyun.pic.wf.domain.ProcessDefinition;
import com.deyuanyun.pic.wf.domain.ProcessDefinitionExample;
import com.deyuanyun.pic.wf.domain.ProcessInstance;
import com.deyuanyun.pic.wf.domain.ProcessInstanceExample;
import com.deyuanyun.pic.wf.domain.TaskInstance;
import com.deyuanyun.pic.wf.domain.TaskInstanceExample;
import com.deyuanyun.pic.wf.mapper.NodeMapper;
import com.deyuanyun.pic.wf.mapper.ProcessDefinitionMapper;
import com.deyuanyun.pic.wf.mapper.ProcessInstanceMapper;
import com.deyuanyun.pic.wf.mapper.RepairWorkflowMapper;
import com.deyuanyun.pic.wf.mapper.TaskInstanceMapper;
import com.deyuanyun.pic.wf.service.RepairWorkflowService;


@Service
public class RepairWorkflowServiceImpl implements RepairWorkflowService {

    //TODO 未来：可以把这个地方做成从配置文件注入的
    /**
     * 换管流程定义编号，因为需要判断该流程是否为换管
     */
    private String REPLACE_PIPE_DEF_ID = "XXXEEEFFFEEESSSFFFEEEWWWCCCRRR06";
    
    /**
     * 换管的节点编号，因为在换管时候需要跳过修复流程至换管节点处。
     */
    private String REPLACE_PIPE_NODE_ID = "1E3290704BF1749EE0530E0AA8C00075";
    
    
    
    private @Autowired ProcessDefinitionMapper processDefinitionMapper;//维修流程定义
    private @Autowired ProcessInstanceMapper processInstanceMapper;//维修流程定义与节点的关联
    private @Autowired NodeMapper nodeMapper;//维修节点
    private @Autowired RepairWorkflowMapper repairWorkflowMapper;//维修流程相关
    private @Autowired TaskInstanceMapper taskInstanceMapper;//维修任务实例

    public void createOrUpdate(ProcessDefinition processDefinition,List<ProcessInstance> processInstanceList) {
        
        String name = processDefinition.getName();
        String pdid = processDefinition.getId();
        ProcessDefinition pd = queryProcessDefinitionByNameNotId(name, pdid);
        Asserts.assertTrue(pd == null, name + " 已存在。");//验证流程名称是否重复

        String definitionId = processDefinition.getId();
        if(StringUtils.isEmpty(definitionId)){
            processDefinition.setAllowDeleted((short)0);//用户创建的流程允许删除
            processDefinition.setCode("OTHER_REPAIR_WF");//用户创建的流程都是属于OTHER_REPAIR_WF
            DomainInitUtil.initCreateDomain(processDefinition);
            processDefinitionMapper.insertSelective(processDefinition);

            definitionId = processDefinition.getId();
        }else{
            DomainInitUtil.initUpdateDomain(processDefinition);
            processDefinitionMapper.updateByPrimaryKeySelective(processDefinition);
        }

        ProcessDefinition processDefinition2 = processDefinitionMapper.selectByPrimaryKey(processDefinition.getId());
        String code = processDefinition2.getCode();
        for (int i = 0; i < processInstanceList.size(); i++) {
            ProcessInstance processInstance = processInstanceList.get(i);
            if("OTHER_REPAIR_WF".equals(code)){//如果是自定义流程，那么需要去读Node表中是否可选
                Node node = nodeMapper.selectByPrimaryKey(processInstance.getNodeId());
                processInstance.setNecessary(node.getNecessary());
            }
            String piid = processInstance.getId();
            processInstance.setSort((short)i);//节点顺序
            processInstance.setDefinitionId(definitionId);//流程编号
            if(StringUtils.isEmpty(piid)){
                DomainInitUtil.initCreateDomain(processInstance);
                processInstanceMapper.insertSelective(processInstance);
            }else{
                processInstance.setId(piid);
                DomainInitUtil.initUpdateDomain(processInstance);
                processInstanceMapper.updateByPrimaryKeySelective(processInstance);
            }

        }


    }

    public List<Map<String,Object>> queryProcessInstanceList() {

        List<Map<String,Object>> list = repairWorkflowMapper.queryProcessInstanceList();

        return list;
    }

    public List<Node> queryNodeByNeed() {

        NodeExample nodeExample = new NodeExample();
        nodeExample.createCriteria().andNeedEqualTo((short)1).andDeletedEqualTo((short)0);
        nodeExample.setOrderByClause("id_");
        List<Node> list = nodeMapper.selectByExample(nodeExample);
        return list;
    }

    @Cacheable(value = "tempStoreData")
    @Override
    public Node queryNode(String nodeId) {
        Node node = nodeMapper.selectByPrimaryKey(nodeId);
        return node;
    }

    @Override
    public void createOrUpdateTaskInstance(String def_id, String defectInfoId) {
        
        //获取指定的目标流程信息
        List<ProcessInstance> processInstanceList = queryProcessInstance(def_id);
        
        //获取原维修流程
        List<TaskInstance> taskInstanceList = queryTaskInstance(defectInfoId);
        
        //顺序号，变更维修流程的时候，如果原流程很长，目标流程很短，则直接用目标流程中的顺序号可能会变成插入到原流程中，而不是拼接在原流程最后。
        short sort = 0;
        
        if(taskInstanceList == null || taskInstanceList.isEmpty()){
            //走创建
        }else{
            //走修改
            //注意1：processInstanceList、taskInstanceList两者必须是有序的，已完成在前，未完成在后，且按照流程定义顺序排列。
            //注意2：拼接流程的依据是拿原流程中最后一个已完成去目标流程中找，找到就拼接上，如果未找到，就拿原流程已完成的上一个去目标流程中找。类推。
            
            //跳过已做的节点（找到一个未完成的节点，并且目标流程中必须有此节点）
            for (int i = taskInstanceList.size() - 1; i >= 0; i--) {
                TaskInstance ti = taskInstanceList.get(i);

                if(1 == ti.getIsCompletion()){//如果已完成了该节点，则无需处理该节点，所以移除该节点。
                    taskInstanceList.remove(i);//
                    int ide = Collections.binarySearch(processInstanceList, ti, new DomainComparator("nodeId"));
                    if(ide > -1){//如果在目标节点中任然存在该节点，则移除该节点之前的所有节点。
                        ProcessInstance pi = processInstanceList.get(ide);
                        sort = pi.getSort();
                        processInstanceList = processInstanceList.subList(ide + 1, processInstanceList.size());
                        taskInstanceList = taskInstanceList.subList(i, taskInstanceList.size());//这个地方不+1，是因为上面有一个remove。
                        break;
                    }
                } else {//如果该节点未完成，则继续向上找
                    
                    //break;
                }
                
            }
            if(REPLACE_PIPE_DEF_ID.equals(def_id)){
                //换管流程，需要跳过诸多节点直接到换管节点
                for (int i = 0; i < processInstanceList.size(); i++) {
                    ProcessInstance pi = processInstanceList.get(i);
                    if(REPLACE_PIPE_NODE_ID.equals(pi.getNodeId())){
                        //如果是换管节点了，就停止跳过
                        break;
                    } else{
                        processInstanceList.remove(i--);
                    }
                }
            }
            
            //还剩余在里面的对象为：未完成的节点，需要被替换掉，所以标记删除。
            for (int j = 0; j < taskInstanceList.size(); j++) {
                TaskInstance ti = taskInstanceList.get(j);
                DomainInitUtil.initUpdateDomain(ti,"换维修流程>删除该节点");
                ti.setDeleted((short)1);
                taskInstanceMapper.updateByPrimaryKeySelective(ti);
            }
        }


        //把目标流程写入到对应缺陷点的流程实例中
        for (int i = 0; i < processInstanceList.size(); i++) {
            ProcessInstance processInstance = processInstanceList.get(i);
            Short isPerform = processInstance.getIsPerform();
            if(isPerform != null && isPerform.intValue() == 1){//只保存需要支持的流程节点
                TaskInstance taskInstance = processInstanceToTaskInstance(processInstance);
                DomainInitUtil.initCreateDomain(taskInstance);
                taskInstance.setIsCompletion((short)0);//未完成维修
                taskInstance.setDefectInfoId(defectInfoId);//对应缺陷点
                taskInstance.setJumpNode((short)0);
                //因为目前业务流程定义个数会完全相同，所以就直接用流程定义中的排序号sort。
                taskInstance.setSort(processInstance.getSort());//(short)(sort + i)
                taskInstanceMapper.insertSelective(taskInstance);
            }
            
        }
        
    }
    
    private TaskInstance processInstanceToTaskInstance(ProcessInstance processInstance){
        
        String nodeId = processInstance.getNodeId();
        Node node = queryNode(nodeId);
        
        TaskInstance ti = new TaskInstance();
        ti.setNodeId(nodeId);
        ti.setNodeName(processInstance.getNodeName());
        ti.setTabName(node.getTabName());
        ti.setNodeType(node.getNodeType());
        return ti;
    }

    @Override
    public List<TaskInstance> queryTaskInstance(String defectInfoId) {
        
        TaskInstanceExample example = new TaskInstanceExample();
        example.createCriteria().andDefectInfoIdEqualTo(defectInfoId).andDeletedEqualTo((short)0);
        example.setOrderByClause("sort");
        List<TaskInstance> taskInstanceList = taskInstanceMapper.selectByExample(example);
        
        return taskInstanceList;
    }

    @Override
    public List<ProcessInstance> queryProcessInstance(String definitionId) {
        
        ProcessInstanceExample example = new ProcessInstanceExample();
        example.createCriteria().andDefinitionIdEqualTo(definitionId);//.andDeletedEqualTo((short)0)//去掉了这个删除判断，因为开始维修的时候，流程可能已经被删了。
        example.setOrderByClause("sort");
        List<ProcessInstance> processInstanceList = processInstanceMapper.selectByExample(example);
        
        return processInstanceList;
    }

    @Override
    public ProcessDefinition queryProcessDefinitionByNameNotId(String name,String id) {
        
        ProcessDefinitionExample example = new ProcessDefinitionExample();
        ProcessDefinitionExample.Criteria c = example.createCriteria().andNameEqualTo(name).andDeletedEqualTo((short)0);
        if(StringUtils.isNotEmpty(id)){
            c.andIdNotEqualTo(id);
        }
        
        List<ProcessDefinition> pd_list = processDefinitionMapper.selectByExample(example);
        if(pd_list == null || pd_list.isEmpty()){
            return null;
        }
        return pd_list.get(0);
    }

    
    @Cacheable(value = "tempStoreData")
    @Override
    public ProcessDefinition queryProcessDefinitionById(String definitionId) {
        
        ProcessDefinition processDefinition = processDefinitionMapper.selectByPrimaryKey(definitionId);
        return processDefinition;
    }
    
    
    
    /*
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(3);
        for (int i = 0; i < 10; i++) {
            list.add("");
        }
        System.out.println(list.size());
        double d = 20014999;
        BigDecimal b = new BigDecimal("20014999.454");
        b = b.setScale(2, RoundingMode.HALF_UP);
        System.out.println(d + 1);
        System.out.println(b.toString());
    }
     */
}
